package com.aaron.serialize.Kyro;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;

import com.aaron.exception.SerializeException;
import com.aaron.remoting.dto.RpcRequest;
import com.aaron.remoting.dto.RpcResponse;
import com.aaron.serialize.Serializer;

import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

// 这里本该是调用spi扩展来实现序列化与反序列化的，这里直接使用的是kryo
@Slf4j
public class KryoSerializer implements Serializer {

    // 因为kryo是线程不安全的，所以使用本地线程变量ThreadLocal
    private final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
        // 在threadLocalMap中保存一个kryo对象
        Kryo kryo = new Kryo();
        // 通过注册kryo可以将类的全限定名抽象为一个数字，即用一个数字代表全限定名
        kryo.register(RpcResponse.class);
        kryo.register(RpcRequest.class);
        return kryo;
    });

    // 实现序列化接口，RpcMessage消息需要编码时使用到
    @Override
    public byte[] serialize(Object obj) {
        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                Output output = new Output(byteArrayOutputStream)) {
            Kryo kryo = kryoThreadLocal.get();
            // Object->byte:将对象序列化为byte数组
            kryo.writeObject(output, obj);
            kryoThreadLocal.remove(); // 用完记得释放，避免OOM
            return output.toBytes();
        } catch (Exception e) {
            throw new SerializeException("Serialization failed");
        }
    }

    // 实现反序列化接口,RpcMessage消息需要解码时使用到
    @Override
    public <T> T deserialize(byte[] bytes, Class<T> clazz) {
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
                Input input = new Input(byteArrayInputStream)) {
            Kryo kryo = kryoThreadLocal.get();
            // byte->Object:从byte数组中反序列化出对对象
            Object o = kryo.readObject(input, clazz);
            kryoThreadLocal.remove();
            return clazz.cast(o);
        } catch (Exception e) {
            throw new SerializeException("Deserialization failed");
        }
    }

}
